package com.ideaaedi.commonspring.register;

import com.ideaaedi.commonds.constants.BeanNameConstant;
import com.ideaaedi.commonspring.aop.*;
import com.ideaaedi.commonspring.injector.ExtAutowiredInjector;
import com.ideaaedi.commonspring.listener.DefaultRateLimiterExpireListener;
import com.ideaaedi.commonspring.lite.EnableFeature;
import com.ideaaedi.commonspring.lite.register.FeatureRegistrarProxy;
import com.ideaaedi.commonspring.lock.DefaultRedisLockSupport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;

import java.util.Map;
import java.util.Objects;

/**
 * 功能注册器
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2021.0.5.A
 */
@Slf4j
public class CommonSpringFeatureRegistrar implements FeatureRegistrarProxy {
    
    /**
     * 注册bean
     */
    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata annotationMetadata, @NonNull BeanDefinitionRegistry registry) {
        Map<String, Object> attributeMap = annotationMetadata.getAnnotationAttributes(EnableFeature.class.getName());
        Objects.requireNonNull(attributeMap, "attributeMap should not be null.");
        boolean enableAntiDuplicate = (boolean) attributeMap.get("enableAntiDuplicate");
        boolean enableLockAnno = (boolean) attributeMap.get("enableLockAnno");
        boolean enableExtAutowiredInjector = (boolean) attributeMap.get("enableExtAutowiredInjector");
        AnnotationAttributes[] parameterRecorderArr = (AnnotationAttributes[]) attributeMap.get("enableParameterRecorder");
        AnnotationAttributes[] timeWatcherArr = (AnnotationAttributes[]) attributeMap.get("enableTimeWatcher");
        AnnotationAttributes[] rateLimiterConfigArr = (AnnotationAttributes[]) attributeMap.get("enableRateLimiter");
        // 注册 AntiDuplicationAdvice
        if (enableAntiDuplicate) {
            registerAntiDuplicationAop(registry);
        }
        // 注册分布式锁注解aop
        if (enableLockAnno) {
            registerLockAop(registry);
        }
        // 注册 ExtAutowiredInjector.ExtAutowiredInjectorProcessor
        if (enableExtAutowiredInjector) {
            registerExtAutowiredInjector(registry);
        }
        // 注册 ParameterRecorderAdvice
        if (parameterRecorderArr != null && parameterRecorderArr.length > 0) {
            AnnotationAttributes annotationAttributes = parameterRecorderArr[0];
            registerParameterRecorderAdvice(registry, annotationAttributes);
        }
        // 注册 TimeWatcherAdvice
        if (timeWatcherArr != null && timeWatcherArr.length > 0) {
            AnnotationAttributes annotationAttributes = timeWatcherArr[0];
            registerTimeWatcher(registry, annotationAttributes);
        }
        // 注册 限流器
        if (rateLimiterConfigArr != null && rateLimiterConfigArr.length > 0) {
            AnnotationAttributes annotationAttributes = rateLimiterConfigArr[0];
            registerRateLimiterAdvice(registry, annotationAttributes);
        }
    }
    
    /**
     * 注册防重复提交注解aop
     */
    protected void registerAntiDuplicationAop(BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(AntiDuplicationPointcutAdvisor.class);
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        registry.registerBeanDefinition(AntiDuplicationPointcutAdvisor.BEAN_NAME, builder.getBeanDefinition());
        log.info("registry bean '{}' with class '{}'", AntiDuplicationPointcutAdvisor.BEAN_NAME, AntiDuplicationPointcutAdvisor.class.getName());
    }
    
    /**
     * 注册分布式锁注解aop
     */
    protected void registerLockAop(BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DefaultRedisLockSupport.SynchronizedAdvice.class);
        registry.registerBeanDefinition(DefaultRedisLockSupport.SynchronizedAdvice.BEAN_NAME, builder.getBeanDefinition());
        log.info("registry bean '{}' with class '{}'", DefaultRedisLockSupport.SynchronizedAdvice.BEAN_NAME, DefaultRedisLockSupport.SynchronizedAdvice.class.getName());
    }
    
    /**
     * 注册 ExtAutowiredInjector.ExtAutowiredInjectorProcessor
     */
    protected void registerExtAutowiredInjector(BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ExtAutowiredInjector.ExtAutowiredInjectorProcessor.class);
        registry.registerBeanDefinition(BeanNameConstant.BEAN_NAME_4_ExtAutowiredInjectorProcessor, builder.getBeanDefinition());
        log.info("registry bean '{}' with class '{}'", BeanNameConstant.BEAN_NAME_4_ExtAutowiredInjectorProcessor, ExtAutowiredInjector.ExtAutowiredInjectorProcessor.class.getName());
    }
    
    /**
     * 注册 ParameterRecorderAdvice
     */
    protected void registerParameterRecorderAdvice(@NonNull BeanDefinitionRegistry registry,
                                                 @NonNull AnnotationAttributes annotationAttributes) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ParameterRecorderAdviceConfig.class);
        builder.addConstructorArgValue(annotationAttributes);
        registry.registerBeanDefinition(ParameterRecorderAdviceConfig.BEAN_NAME, builder.getBeanDefinition());
    }
    
    /**
     * 注册 TimeWatcher
     */
    protected void registerTimeWatcher(@NonNull BeanDefinitionRegistry registry,
                                       @NonNull AnnotationAttributes annotationAttributes) {
        // 注册aop
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TimeWatcherAdviceConfig.class);
        builder.addConstructorArgValue(annotationAttributes);
        registry.registerBeanDefinition(TimeWatcherAdviceConfig.BEAN_NAME, builder.getBeanDefinition());
        
        // 注册filter
        boolean disableFilter = (boolean)annotationAttributes.get("disableFilter");
        if (!disableFilter) {
            builder = BeanDefinitionBuilder.genericBeanDefinition(TimeWatcherFilterConfig.class);
            builder.addConstructorArgValue(annotationAttributes);
            registry.registerBeanDefinition(TimeWatcherFilterConfig.BEAN_NAME, builder.getBeanDefinition());
        }
    }

    /**
     * 注册 限流器
     */
    protected void registerRateLimiterAdvice(@NonNull BeanDefinitionRegistry registry,
                                             @NonNull AnnotationAttributes annotationAttributes) {
        int aopPriority = (int)annotationAttributes.get("aopPriority");
        boolean enableDefaultExpireListener = (boolean)annotationAttributes.get("enableDefaultExpireListener");
        long expireDuration = (long)annotationAttributes.get("expireDuration");
        expireDuration = expireDuration <= 0 ? 0 : expireDuration;

        // 限流器
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RateLimiterAdvice.class);
        builder.addConstructorArgValue(aopPriority);
        registry.registerBeanDefinition(RateLimiterAdvice.BEAN_NAME, builder.getBeanDefinition());
        log.info("registry bean '{}' with class '{}'", RateLimiterAdvice.BEAN_NAME, RateLimiterAdvice.class.getName());
        // 相关redis键过期监听处理器
        if (enableDefaultExpireListener) {
            builder = BeanDefinitionBuilder.genericBeanDefinition(DefaultRateLimiterExpireListener.class);
            builder.addConstructorArgValue(expireDuration);
            registry.registerBeanDefinition(DefaultRateLimiterExpireListener.BEAN_NAME, builder.getBeanDefinition());
            log.info("registry bean '{}' with class '{}'", DefaultRateLimiterExpireListener.BEAN_NAME, DefaultRateLimiterExpireListener.class.getName());
        }
    }
    
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 100;
    }
}
